package cn.tedu.truetaste.service.impl;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.tedu.truetaste.ex.ServiceException;
import cn.tedu.truetaste.mapper.OrderItemMapper;
import cn.tedu.truetaste.mapper.OrderMapper;
import cn.tedu.truetaste.mapper.SkuMapper;
import cn.tedu.truetaste.pojo.dto.OrderAddDTO;
import cn.tedu.truetaste.pojo.dto.OrderItemAddDTO;
import cn.tedu.truetaste.pojo.dto.OrderListTimeDTO;
import cn.tedu.truetaste.pojo.dto.OrderUpdateDTO;
import cn.tedu.truetaste.pojo.entity.Order;
import cn.tedu.truetaste.pojo.entity.OrderItem;
import cn.tedu.truetaste.pojo.entity.Shop;
import cn.tedu.truetaste.pojo.vo.OrderAddVO;
import cn.tedu.truetaste.pojo.vo.OrderDetailVO;
import cn.tedu.truetaste.pojo.vo.OrderListVO;
import cn.tedu.truetaste.restful.JsonPage;
//import cn.tedu.truetaste.security.LoginPrincipal;
import cn.tedu.truetaste.service.IOrderService;
import cn.tedu.truetaste.service.IShopService;
import cn.tedu.truetaste.util.IdUtils;
import cn.tedu.truetaste.web.ServiceCode;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
//import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Slf4j
@Service
public class OrderServiceImpl implements IOrderService {

    @Value("${wx.appId}")
    private String appId;

    @Value("${wx.secret}")
    private String secret;

    private String openIdData;

    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private IShopService shopService;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;

    public OrderAddVO addOrder(OrderAddDTO orderAddDTO) {

        // 第一部分: 收集信息,准备数据
        // 先收集订单信息,实例化Order对象
        Order order=new Order();
        // 将参数OrderAddDTO同名属性赋值给order对象
        BeanUtils.copyProperties(orderAddDTO,order);
        // 参数orderAddDTO中信息并不完整,有些信息需要我们生成或收集
        // 编写一个专门收集信息的方法,完成信息收集的工作
        loadOrder(order);
        // 到此为止,order对象的所有属性就收集完毕了
        // 下面要将参数orderAddDTO中包含的订单项的集合:orderItems进行信息的收集工作
        List<OrderItemAddDTO> orderItems=orderAddDTO.getOrderItems();
        if(orderItems == null  ||  orderItems.isEmpty()){
            // 如果参数orderAddDTO中包含的订单项集合是null或没有元素
            // 抛出异常终止程序
            throw new ServiceException(
                    ServiceCode.ERR_BAD_REQUEST,"订单中至少要包含一件商品");
        }

        // 我们最终目标是将订单项信息保存到数据库
        // 数据库中编写的方法参数类型是List<OrderItem>
        // 所以我们要遍历orderItems集合中的每个元素,转换为OrderItem
        // 然后再保存再List<OrderItem>类型的集合中才能完成订单项的新增
        // 先实例化List<OrderItem>的集合
        List<OrderItem> orderItemList=new ArrayList<>();
        // 遍历DTO中的集合
        for(OrderItemAddDTO addDTO : orderItems){
            // 实例化目标对象OrderItem
            OrderItem orderItem=new OrderItem();
            // 将addDTO的同名属性赋值到OrderItem对象中
            BeanUtils.copyProperties(addDTO,orderItem);
            // 将addDTO对象没有的id属性和orderId属性赋值
            // id属性还是通过Leaf获取

            System.out.println("------------------------------------------");
            //Long itemId=IdGeneratorUtils.getDistributeId("order_item");
            //orderItem.setId(itemId);
            orderItem.setId(IdUtils.generateId());
            // 订单id直接从order对象中获取
            orderItem.setOrderId(order.getId());
            orderItemList.add(orderItem);

            // 第二部分: 执行新增订单相关数据库操作指令
            // 1.减少库存
            // 当前正在遍历的对象是一个包含skuId和购买数量的对象
            // 先获取skuId
            Long skuId=orderItem.getSkuId();
            System.out.println("-------------------"+skuId);
            //调用sku模块写好的减少库存的方法
            int num = skuMapper.reduceStockNum(skuId, orderItem.getNum());
            System.out.println("------------------"+num);
            // 判断num是否为0
            if(num == 0){
                log.error("菜品库存不足,skuId:{}",skuId);
                // 抛出异常,终止程序
                throw new ServiceException(
                        ServiceCode.ERR_BAD_REQUEST,"库存不足");
            }
            // 2.删除用户在购物车中勾选的商品信息
            Shop shop = new Shop();
            shop.setUserId(order.getUserId());
            shop.setSkuId(skuId);
            // 执行删除
            shopService.removeUserShops(shop);
        }

        // 3.执行新增订单
        orderMapper.insert(order);
        // 4.执行新增订单项(批量新增集合中所有订单项数据)
        orderItemMapper.insertOrderItemList(orderItemList);
        // 第三部分: 准备返回值,返回给前端
        // 实例化返回值类型对象
        OrderAddVO addVO=new OrderAddVO();
        addVO.setId(order.getId());
        addVO.setOrderNumber(order.getOrderNumber());
        addVO.setCreateTime(order.getGmtCreate());
        addVO.setPayPrice(order.getPayPrice());
        // 最后千万别忘了返回!!!!
        return addVO;

    }

    @Override
    public void updateOrderState(OrderUpdateDTO orderUpdateDTO) {

        // 先实例化Order对象
        Order order=new Order();
        // orderUpdateDTO参数中,id和status属性赋值到order对象
        BeanUtils.copyProperties(orderUpdateDTO,order);
        // 调用动态修改方法,按给定的id做条件,修改订单状态为status的值
        orderMapper.updateOrderById(order);

    }

    @Override
    public JsonPage<OrderListVO> listOrdersBetweenTimes(OrderListTimeDTO orderListTimeDTO) {
        // 方法开始,需要在运行查询前先判断用户给定的时间范围
        // 判断orderListTimeDTO中开始时间和结束时间是否有效,编写一个方法专门做这个业务
        validateTimes(orderListTimeDTO);
        // 设置分页条件
        PageHelper.startPage(orderListTimeDTO.getPage(),
                orderListTimeDTO.getPageSize());
        // 将用户id赋值到orderListTimeDTO中
        //orderListTimeDTO.setUserId(getUserId());
        orderListTimeDTO.setOpenId(getUserOpenId(openIdData));
        // 执行查询
        List<OrderListVO> list=orderMapper
                .selectOrdersBetweenTimes(orderListTimeDTO);
        // 别忘了返回
        return JsonPage.restPage(new PageInfo<>(list));
    }

    private void validateTimes(OrderListTimeDTO orderListTimeDTO) {
        // 先获取用户提供的开始和结束时间
        LocalDateTime start=orderListTimeDTO.getStartTime();
        LocalDateTime end=orderListTimeDTO.getEndTime();
        // 为了使业务不太复杂,我们设计start或end任意属性为null,就查询最近一个月订单
        if(start == null || end == null){
            // 开始时间设置为当前时间的一个月之前
            start=LocalDateTime.now().minusMonths(1);
            // 结束时间设置为当前时间
            end=LocalDateTime.now();
            // 将设置好的值赋值到参数中
            orderListTimeDTO.setStartTime(start);
            orderListTimeDTO.setEndTime(end);
        }else{
            // 如果start和end都非null
            // 判断如果end小于start就要抛出异常
            // if(end.isBefore(start)){
            if(end.toInstant(ZoneOffset.of("+8")).toEpochMilli()<
                    start.toInstant(ZoneOffset.of("+8")).toEpochMilli()){
                // 结束时间小于开始时间,可以抛出异常
                throw new ServiceException(
                        ServiceCode.ERR_BAD_REQUEST,"结束时间应大于开始时间!");
            }
        }
    }

    @Override
    public OrderDetailVO getOrderDetail(Long id) {
        return null;
    }

    public String getUserOpenId(String code) {
        String authUrl="https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code";
        authUrl = authUrl + "&appid=" + appId + "&secret=" + secret + "&js_code=" + code;
        System.out.println("30000,"+authUrl);
        String result = HttpUtil.get(authUrl);

        JSONObject jsonObject = JSONUtil.parseObj(result);
        System.out.println(jsonObject);
        String openId = jsonObject.getStr("openid");
        openIdData = openId;
        return openId;
    }

//    public LoginPrincipal getUserInfo(){
//        // 获取SpringSecurity上下文
//        UsernamePasswordAuthenticationToken authenticationToken=
//                (UsernamePasswordAuthenticationToken)
//                        SecurityContextHolder.getContext().getAuthentication();
//
//        if(authenticationToken == null){
//            String message = "您还未登陆!";
//            log.warn(message);
//            throw new ServiceException(
//                    ServiceCode.ERR_UNAUTHORIZED,message);
//        }
//        // 从SpringSecurity上下文中获取用户信息
//        LoginPrincipal authenticationInfo =
//                (LoginPrincipal) authenticationToken.getPrincipal();
//
//        // 最终返回用户信息
//        return authenticationInfo;
//    }
//
//    private Long getUserId() {
//        return getUserInfo().getId();
//    }

    private void loadOrder(Order order) {
        // 先通过Leaf分布式序列化生成系统来获取id
        //Long id= IdGeneratorUtils.getDistributeId("order");
        order.setId(IdUtils.generateId());
        // 生成用户看到的订单编号,也就是UUID
        order.setOrderNumber(UUID.randomUUID().toString());
        // 赋值userId
        if(order.getOpenId()==null){
            // SpringSecurity上下文中获取用户id
            //order.setUserId(getUserId());
            order.setOpenId(getUserOpenId(openIdData));
        }
        // 可以将其他的可能为null的属性赋值
        // 订单状态可能为null,判断订单的状态,如果为null默认赋值0
        if(order.getStatus()==null){
            order.setStatus(0);
        }
        // 为了保证当前下单时间gmt_order和数据生成时间gmt_create一致
        // 我们为下列属性赋相同的值
        LocalDateTime now= LocalDateTime.now();
        order.setGmtOrder(now);
        order.setGmtCreate(now);
        order.setGmtModified(now);

        // 后端代码可以对前端传入的金额进行验算
        // 验算公式: 实际支付金额=原价-优惠
        Double price=order.getPrice();
        Double offerCouponsPrice=order.getOfferCouponsPrice();
        Double payPrice= price - offerCouponsPrice;
        // 赋值到order对象中
        order.setPayPrice(payPrice);

    }

}
